#!/usr/bin/sh
#
# vopstat - Trace the vnode interface.
#           Written using DTrace (Solaris 10 3/05)
#
# Author: Richard McDougall
#
# $Id: vopstat 3 2007-08-01 10:50:08Z brendan $
#
# USAGE:	vopstat [-t] [/mountname]
#
#		vopstat		# default output, summary each 5 secs
#		-t 		# trace activity as it occurs
#
# Example:
#
# ./vopstat
# 
# VOP Physical IO                                                   Count
# fop_fsync                                                           236
# 
# VOP Count                                                         Count
# fop_create                                                            1
# fop_fid                                                               1
# fop_lookup                                                            2
# fop_access                                                            3
# fop_read                                                              3
# fop_poll                                                             11
# fop_fsync                                                            31
# fop_putpage                                                          32
# fop_ioctl                                                           115
# fop_write                                                           517
# fop_rwlock                                                          520
# fop_rwunlock                                                        520
# fop_inactive                                                        529
# fop_getattr                                                        1057
# 
# VOP Wall Time                                                  mSeconds
# fop_fid                                                               0
# fop_access                                                            0
# fop_read                                                              0
# fop_poll                                                              0
# fop_lookup                                                            0
# fop_create                                                            0
# fop_ioctl                                                             0
# fop_putpage                                                           1
# fop_rwunlock                                                          1
# fop_rwlock                                                            1
# fop_inactive                                                          1
# fop_getattr                                                           2
# fop_write                                                            22
# fop_fsync                                                           504
# 
# COPYRIGHT: Copyright (c) 2006 Richard McDougall
#
# CDDL HEADER START
#
#  The contents of this file are subject to the terms of the
#  Common Development and Distribution License, Version 1.0 only
#  (the "License").  You may not use this file except in compliance
#  with the License.
#
#  You can obtain a copy of the license at Docs/cddl1.txt
#  or http://www.opensolaris.org/os/licensing.
#  See the License for the specific language governing permissions
#  and limitations under the License.
#
# CDDL HEADER END
#
# Shell Wrapper Concept by Brendan Gregg
#
# 08-Jan-2006	Richard McDougall	Created this.
# 23-Apr-2006	Brendan Gregg		Minor style tweaks.
# 23-Apr-2006	   "      "		Last update.


##############################
# --- Process Arguments ---
#

### default variables
opt_trace=0; opt_fs=0; opt_stats=1; opt_all=0

### process options
while getopts t name
do
	case $name in
	t)	opt_trace=1 ;;
	h|?)	cat <<-END >&2
		USAGE: voptrace [-t] [/mountpoint]
		        voptrace         # default output
		                 -t      # trace
		   eg,
		        voptrace -t      # trace all file systems
		        voptrace -t /tmp # trace /tmp
		        voptrace  /tmp   # summary stats for /tmp
		END
		exit 1
	esac
done
shift `expr $OPTIND - 1`
filesys="$1"

### option logic
if [ $opt_trace -eq 1 ]; then
	opt_stats=0
fi
if [ -z "$filesys" ]; then
	opt_all=1
fi

#################################
# --- Main Program, DTrace ---
#
/usr/sbin/dtrace -n '
 /*
  * Command line arguments
  */
 inline int OPT_fs 	= '$opt_fs';
 inline int OPT_all 	= '$opt_all';
 inline int OPT_trace 	= '$opt_trace';
 inline int OPT_stats 	= '$opt_stats';
 inline string FILESYS 	= "'$filesys'";
 
 #pragma D option quiet

 /*
  * Print header
  */
 dtrace:::BEGIN 
 {
	last_event[""] = 0;

	/* print main headers */
	OPT_stats == 1 ?
		printf("\033[H\033[2J") : 1;

	OPT_trace == 1 ?
		printf("%2s %-15s %-10s %51s %2s %8s %8s\n", 
		    "", "Event", "Device", "Path", "RW", "Size", "Offset") : 1;
	self->path = "";
	self->trace = 0;
 }

 dtrace:::BEGIN
 /OPT_trace == 1/
 {
	/* make D compiler happy */
	@vop_iocnt[""] = count();
	@vop_cnt[""]   = count();
	@vop_time[""]  = sum(0);
	trunc(@vop_iocnt);
	trunc(@vop_cnt);
	trunc(@vop_time);
 }
 
 fbt::fop_*:entry
 {
 	self->trace = 0;

 	/* Get vp: fop_open has a pointer to vp */
 	this->vpp = (vnode_t **)arg0;
 	self->vp = (vnode_t *)arg0;
 	self->vp = probefunc == "fop_open" ? (vnode_t *)*this->vpp : self->vp;

 	/* And the containing vfs */
         this->vfsp = self->vp ? self->vp->v_vfsp : 0;

 	/* And the paths for the vp and containing vfs */
 	this->vfsvp = this->vfsp ?
	    (struct vnode *)((vfs_t *)this->vfsp)->vfs_vnodecovered : 0;
 	self->vfspath = this->vfsvp ? stringof(this->vfsvp->v_path) : "unknown";
 
 	/* Check if we should trace the root fs */
 	(OPT_all || 
 	    (FILESYS == "/" && this->vfsp &&
 	    (this->vfsp == `rootvfs))) ? self->trace = 1 : self->trace;
 
 	/* Check if we should trace the fs */
 	(OPT_all || (self->vfspath == FILESYS)) ? self->trace = 1 : self->trace;

	self->vfspath = 0;
 }
 
 /*
  * Trace the entry point to each fop
  */ 
 fbt::fop_*:entry
 /self->trace/
 {
 	self->path = (self->vp != NULL && self->vp->v_path) ?
	    stringof(self->vp->v_path) : "unknown";
 
 	/* Some fops has the len in arg2 */
 	(probefunc == "fop_getpage" ||
 	 probefunc == "fop_putpage" ||
 	 probefunc == "fop_none") ? self->len = arg2 : 1; 
 
 	/* Some fops has the len in arg3 */
 	(probefunc == "fop_pageio" ||
 	 probefunc == "fop_none") ? self->len = arg3 : 1; 
 
 	/* Some fops has the len in arg4 */
 	(probefunc == "fop_addmap" ||
 	 probefunc == "fop_map" ||
 	 probefunc == "fop_delmap") ? self->len = arg4 : 1; 
 
 	/* Some fops has the offset in arg1 */
 	(probefunc == "fop_addmap" ||
 	 probefunc == "fop_map" ||
 	 probefunc == "fop_getpage" ||
 	 probefunc == "fop_putpage" ||
 	 probefunc == "fop_seek" ||
 	 probefunc == "fop_delmap") ? self->off = arg1 : 1; 
 
 	/* Some fops has the offset in arg3 */
 	(probefunc == "fop_close" ||
 	 probefunc == "fop_pageio") ? self->off = arg3 : 1; 
 
 	/* Some fops has the offset in arg4 */
 	probefunc == "fop_frlock" ? self->off = arg4 : 1; 
 
 	/* Some fops has the pathname in arg1 */
 	self->path = (probefunc == "fop_create" ||
 	 probefunc == "fop_mkdir" ||
 	 probefunc == "fop_rmdir" ||
 	 probefunc == "fop_remove" ||
 	 probefunc == "fop_lookup") ?
 	    strjoin(self->path, strjoin("/", stringof(arg1))) : self->path;
 
	OPT_trace ?
 		printf("%2s %-15s %-10s %51s %2s %8d %8d\n", 
 		    "->", probefunc, "-", self->path, "-",
		    self->len, self->off) : 1;

 	self->type = probefunc;
 	self->vop_entry[probefunc] = timestamp;
 }
 
 fbt::fop_*:return
 /self->trace == 1/
 {
	OPT_trace ?
 		printf("%2s %-15s %-10s %51s %2s %8d %8d\n", 
		    "<-", probefunc, "-", self->path, "-",
		    self->len, self->off) : 1;

	OPT_stats == 1 ?
 		@vop_time[probefunc] =
			sum(timestamp - self->vop_entry[probefunc]) : 1;
	OPT_stats == 1 ?
 		@vop_cnt[probefunc] = count() : 1;

	self->path = 0;
	self->len = 0;
	self->off = 0;
 }

 fbt::fop_*:return
 {
	self->trace = 0;
	self->type = 0;
	self->vp = 0;
 }
 
 /* Capture any I/O within this fop */
 io:::start
 /self->trace/
 {
	OPT_stats == 1 ?
 		@vop_iocnt[self->type] = count() : 1;

	OPT_trace == 1?
	 	printf("%2s %-15s %-10s %51s %2s %8d %8u\n",
		    "--", self->type, args[1]->dev_statname,
		    self->path, args[0]->b_flags & B_READ ? "R" : "W",
		    args[0]->b_bcount, args[0]->b_blkno) : 1;
 }
 
 profile:::tick-5s
 /OPT_stats == 1/
 {
	/* Print top 20 only */
 	trunc(@vop_iocnt, 20);
 	trunc(@vop_time, 20);

	/* Display microseconds */
 	normalize(@vop_time, 1000000);
 	printf("\033[H\033[2J");
 	printf("%-60s %10s\n", "VOP Physical IO", "Count");
 	printa("%-60s %10@d\n", @vop_iocnt);
 	printf("\n");
 	printf("%-60s %10s\n", "VOP Count", "Count");
 	printa("%-60s %10@d\n", @vop_cnt);
 	printf("\n");
 	printf("%-60s %10s\n", "VOP Wall Time", "mSeconds");
 	printa("%-60s %10@d\n", @vop_time);

	/* Clear data */
 	trunc(@vop_iocnt);
 	trunc(@vop_cnt);
 	trunc(@vop_time);
 }
'
